/***************************************************************************************

***************************************************************************************/

/*
*************************************************************************************
* File name: block.c
* Function: Description
*
*************************************************************************************
*/


#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <memory.h>

#include "defines.h"
#include "global.h"
#include "elements.h"
#include "block.h"
#include "vlc.h"
#include "golomb_dec.h"
#include "checksyntax.h"
#include "AEC.h" 
#include "intra-prediction.h"
#include "pixel.h"

#define EP (edgepixels+40)

#define int16 int16_t

void idct_dequant_B8(int b8, int b4, int* curr_blk, img_params *img, int bsize)
{
	// inverse transform
	inv_transform_B8(curr_blk, bsize);

	// normalize
	//add_pel_clip(b8, b4, curr_blk, img, bsize);
	g_funs_handle.add_pel_clip(b8, b4, curr_blk, bsize, img->predblk, img->m7, img->pix_y, \
							   img->iStride, img->pix_x, img->pix_c_y, img->pix_c_x, img->iStrideC);
}

/*
*************************************************************************
* Function:Copy region of img->m7 corresponding to block8x8 to curr_blk[][].
* Input:
* Output:
* Return: 
* Attention:img->m7 is [x][y] and curr_blk is normal buffer.
*************************************************************************
*/
void get_curr_blk(int b8, int b4, img_params *img, int* curr_blk, int bsize)
{
	int  xx, yy;
    int  mb_y = 8 * (b8 / 2) + 4 * (b4 / 2);
    int  mb_x = 8 * (b8 % 2) + 4 * (b4 % 2);

    if (b8 < 4) {
        for (yy = 0; yy < bsize; yy++) {
            for (xx = 0; xx < bsize; xx++) {
				curr_blk[yy*bsize+xx] = img->m7[mb_x + xx][mb_y + yy];
            }
        }
    }
    else {
        for (yy = 0; yy < bsize; yy++) {
            for (xx = 0; xx < bsize; xx++) {
				curr_blk[yy*bsize + xx] = img->m8[b8 - 4][xx][yy];
            }
        }
    }
}

/*
*************************************************************************
* Function:Make Intra prediction for all 5 modes for 8*8 blocks.
bs_x and bs_y may be only 4 and 8.
img_x and img_y are pixels offsets in the picture.
* Input:
* Output:
* Return: 
* Attention:
*************************************************************************
*/
int intrapred(img_params *img, uchar_t *pic_y, int img_x,int img_y, int uhBlkWidth)
{
	unsigned char edgepixels[80];
	int x,y;
	unsigned int x_off,y_off;
	int predmode;
	int b4_x,b4_y;
	Macroblock *currMB   = &mb_data[img->current_mb_nr];
	int MBRowSize = img->width / MB_SIZE;
	const int mb_nr    = img->current_mb_nr;   
	
    int x_in_mb = img_x - img->pix_x;
    int y_in_mb = img_y - img->pix_y;

    uchar mb_left_available = (img->pix_x > 0) ? currMB->slice_nr == mb_data[mb_nr - 1].slice_nr : 0; 
    uchar mb_up_available   = (img->pix_y > 0) ? currMB->slice_nr == mb_data[mb_nr - MBRowSize].slice_nr : 0; 
    uchar mb_up_right_available  = (img->pix_y > 0 && img->pix_x + MB_SIZE < img->width ) ? currMB->slice_nr == mb_data[mb_nr - MBRowSize + 1].slice_nr : 0;

    bool bAboveAvail, bLeftAvail, bAboveRightAvail, bLeftDownAvail;
	pel_t *predblk = img->predblk;
	
    if (uhBlkWidth == 4) {
        int x_b8_in_mb = (x_in_mb >> 3) << 3;
        int y_b8_in_mb = (y_in_mb >> 3) << 3;
        int x_in_b8 = x_in_mb - x_b8_in_mb;
        int y_in_b8 = y_in_mb - y_b8_in_mb;
        bool b8_available_up        = (y_b8_in_mb > 0 || mb_up_available);
        bool b8_available_left      = (x_b8_in_mb > 0 || mb_left_available);
        bool b8_available_up_right  = (x_b8_in_mb + 8 == 16) ? (y_b8_in_mb == 0 ? mb_up_right_available : 0) : (y_b8_in_mb == 0 ? mb_up_available : 1);
        bool b8_available_left_down = (x_b8_in_mb == 0 && y_b8_in_mb + 8 != 16) ? b8_available_left : 0;

        bAboveAvail        = (y_in_b8 > 0 || b8_available_up);
        bLeftAvail      = (x_in_b8 > 0 || b8_available_left);
        bAboveRightAvail  = (x_in_b8 + uhBlkWidth == 8) ? (y_in_b8 == 0 ? b8_available_up_right : 0) : (y_in_b8 == 0 ? b8_available_up : 1);
        bLeftDownAvail = (x_in_b8 != 0) ? 0 : (y_in_b8 + uhBlkWidth != 8 ? bLeftAvail : b8_available_left_down);
    } else {
        bAboveAvail        = (y_in_mb > 0 || mb_up_available);
        bLeftAvail      = (x_in_mb > 0 || mb_left_available);
        bAboveRightAvail  = (x_in_mb + uhBlkWidth == 16) ? (y_in_mb == 0 ? mb_up_right_available : 0) : (y_in_mb == 0 ? mb_up_available : 1);
        bLeftDownAvail = (x_in_mb == 0 && y_in_mb + uhBlkWidth != 16) ? 1 : 0;
    }

    x_off = img_x & 0x0FU;
    y_off = img_y & 0x0FU;

    predmode = img->ipredmode[img_x / B4_SIZE + 1][img_y / B4_SIZE + 1];
    assert(predmode>=0);

    b4_x=img_x>>2;
    b4_y=img_y>>2;

    // get reference pixels of intra prediction
    if(bAboveAvail)
    {
		EP[0] = *(pic_y+(img_y-1)*img->iStride + img_x);
        for (x = 0; x < uhBlkWidth; x++) {
            EP[x + 1] = *(pic_y+(img_y - 1)*img->iStride + img_x + x);
        }
        if(bAboveRightAvail)
        {
            for(x=0;x<uhBlkWidth;x++)
                EP[1+x+uhBlkWidth]=*(pic_y+(img_y-1)*img->iStride + img_x+uhBlkWidth+x);
            for(;x<uhBlkWidth;x++)
                EP[1+x+uhBlkWidth]=*(pic_y+(img_y-1)*img->iStride + img_x+uhBlkWidth+uhBlkWidth-1);
        }else
            for(x=0;x<uhBlkWidth;x++)
                EP[1+x+uhBlkWidth]=EP[uhBlkWidth];
        for(;x<uhBlkWidth+2;x++)
            EP[1+x+uhBlkWidth]=EP[uhBlkWidth+x];
    }
    if(bLeftAvail)
    {
		EP[0]=*(pic_y+(img_y)*img->iStride + img_x-1); // copy the left-top pixel 
        for(y=0;y<uhBlkWidth;y++)
            EP[-1-y]=*(pic_y+(img_y+y)*img->iStride + img_x-1);
        if(bLeftDownAvail)
        {
            for(y=0;y<uhBlkWidth;y++)
                EP[-1-y-uhBlkWidth]=*(pic_y+(img_y+uhBlkWidth+y)*img->iStride + img_x-1);
            for(;y<uhBlkWidth;y++)
                EP[-1-y-uhBlkWidth]=*(pic_y+(img_y+uhBlkWidth+uhBlkWidth-1)*img->iStride + img_x-1);
        }
        else{
            for(y=0;y<uhBlkWidth;y++)
                EP[-1-y-uhBlkWidth]=EP[-uhBlkWidth];
		}

        for(;y<uhBlkWidth+2;y++)
            EP[-1-y-uhBlkWidth]=EP[-y-uhBlkWidth];
    }

    if(bAboveAvail&&bLeftAvail)
        EP[0]=*(pic_y+(img_y-1)*img->iStride + img_x-1);

	if (bAboveAvail && !bLeftAvail)  {
		EP[-1] = EP[0];
	}
	if (!bAboveAvail && bLeftAvail)  {
		EP[1] = EP[0];
	}

	predblk = img->predblk + y_off*MB_SIZE + x_off;
	intra_pred_luma(EP, predblk, MB_SIZE, predmode, uhBlkWidth, bAboveAvail, bLeftAvail);

	return DECODING_OK;
}

